﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using log4net;
using BoldBrick.Core.Logging;

namespace BoldBrick.Logging.Log4Net
{
    internal class Log4NetLogger : ILogger
    {
        private log4net.ILog currentLogger = null;

        #region constructor
        public Log4NetLogger(Type type)
        {
            if (!Log4NetLoggerInit.Initialized)
                throw new InvalidOperationException("Logger is not initialized");

            currentLogger = GetLogger(type);
        }

		private static log4net.ILog GetLogger(Type type)
		{
			return log4net.LogManager.GetLogger(type);
        }
        #endregion

        bool ILogger.IsEnabled(LogLevel level)
        {
            return IsEnabled(level);
        }

        void ILogger.Write(LogLevel level, string message, params object[] msgPars)
        {
            Write(level, null, message, msgPars);
        }

        void ILogger.Write(LogLevel level, Exception ex, string message, params object[] msgPars)
        {
            Write(level, ex, message, msgPars);
        }

        void ILogger.LogIfEnabled(LogLevel level, Func<LogDetails> details)
        {
            if (!IsEnabled(level))
                return;

            var d = details.Invoke();
            InnerWrite(level, d.Exception, d.Message);
        }

        #region helpers
        private bool IsEnabled(LogLevel level)
        {
            switch (level)
            {
                case LogLevel.Debug:
                    return currentLogger.IsDebugEnabled;
                case LogLevel.Info:
                    return currentLogger.IsInfoEnabled;
                case LogLevel.Error:
                    return currentLogger.IsErrorEnabled;
                case LogLevel.Warn:
                    return currentLogger.IsWarnEnabled;
                case LogLevel.Fatal:
                    return currentLogger.IsFatalEnabled;
                default:
                    throw new IndexOutOfRangeException("level");
            }
        }

        private void InnerWrite(LogLevel level, Exception ex, string message)
        {
            //if (ex != null && ex.InnerException != null)
            //    InnerWrite(level, ex.InnerException, "Inner exception");

            switch (level)
            {
                case LogLevel.Debug:
                    if (currentLogger.IsDebugEnabled)
                    {
                        if (ex == null)
                            currentLogger.Debug(message);
                        else
                            currentLogger.Debug(message, ex);
                    }
                    break;
                case LogLevel.Info:
                    if (currentLogger.IsInfoEnabled)
                    {
                        if (ex == null)
                            currentLogger.Info(message);
                        else
                            currentLogger.Info(message, ex);
                    }
                    break;
                case LogLevel.Error:
                    if (currentLogger.IsErrorEnabled)
                    {
                        if (ex == null)
                            currentLogger.Error(message);
                        else
                            currentLogger.Error(message, ex);
                    }
                    break;
                case LogLevel.Warn:
                    if (currentLogger.IsWarnEnabled)
                    {
                        if (ex == null)
                            currentLogger.Warn(message);
                        else
                            currentLogger.Warn(message, ex);
                    }
                    break;
                case LogLevel.Fatal:
                    if (currentLogger.IsFatalEnabled)
                    {
                        if (ex == null)
                            currentLogger.Fatal(message);
                        else
                            currentLogger.Fatal(message, ex);
                    }
                    break;
            }
        }

        private void Write(LogLevel level, Exception ex, string message, params object[] msgPars)
        {
            if (message == null)
                throw new ArgumentNullException("message");

            if (msgPars == null)
            {
                InnerWrite(level, ex, message);
                return;
            }

            string msg = null;
            try
            {
                msg = string.Format(message, msgPars);
            }
            catch (FormatException fe)
            {
                InnerWrite(LogLevel.Fatal, fe, "Message formating error!");

                msg = message;
            }

            InnerWrite(level, ex, msg);
        }
                
        #endregion
    }
}
